package com.tjbklx1.redis.chap07;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang3.tuple.Pair;

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction;
import redis.clients.jedis.Tuple;


public class Chapter07 {

	public static final void main(String[] args) {
        new Chapter07().run();
    }

    public void run(){
        Jedis conn = new Jedis("localhost");
        conn.select(15);
        conn.flushDB();

        testIndexDocument(conn);
        testSetOperations(conn);
        testParseQuery(conn);
        testParseAndSearch(conn);
        testSearchWithSort(conn);
        testSearchWithZsort(conn);
        conn.flushDB();

        testStringToScore(conn);
        testIndexAndTargetAds(conn);
        testIsQualifiedForJob(conn);
        testIndexAndFindJobs(conn);
    }

    public void testIndexDocument(Jedis conn) {
        System.out.println("\n----- testIndexDocument -----");

        System.out.println("We're tokenizing some content...");
        Set<String> tokens = Query.tokenize(Query.CONTENT);
        System.out.println("Those tokens are: " +
            Arrays.toString(tokens.toArray()));
        assert tokens.size() > 0;

        System.out.println("And now we are indexing that content...");
        int count =Query. indexDocument(conn, "test", Query.CONTENT);
        assert count == tokens.size();
        Set<String> test = new HashSet<String>();
        test.add("test");
        for (String t : tokens){
            Set<String> members = conn.smembers("idx:" + t);
            assert test.equals(members);
        }
    }

    public void testSetOperations(Jedis conn) {
        System.out.println("\n----- testSetOperations -----");
        Query. indexDocument(conn, "test", Query.CONTENT);

        Set<String> test = new HashSet<String>();
        test.add("test");

        Transaction trans = conn.multi();
        String id = Query.intersect(trans, 30, "content", "indexed");
        trans.exec();
        assert test.equals(conn.smembers("idx:" + id));

        trans = conn.multi();
        id = Query.intersect(trans, 30, "content", "ignored");
        trans.exec();
        assert conn.smembers("idx:" + id).isEmpty();

        trans = conn.multi();
        id = Query.union(trans, 30, "content", "ignored");
        trans.exec();
        assert test.equals(conn.smembers("idx:" + id));

        trans = conn.multi();
        id = Query.difference(trans, 30, "content", "ignored");
        trans.exec();
        assert test.equals(conn.smembers("idx:" + id));

        trans = conn.multi();
        id =Query. difference(trans, 30, "content", "indexed");
        trans.exec();
        assert conn.smembers("idx:" + id).isEmpty();
    }

    public void testParseQuery(Jedis conn) {
        System.out.println("\n----- testParseQuery -----");
        String queryString = "test query without stopwords";
        Query query = Query.parse(queryString);
        String[] words = queryString.split(" ");
        for (int i = 0; i < words.length; i++){
            List<String> word = new ArrayList<String>();
            word.add(words[i]);
            assert word.equals(query.all.get(i));
        }
        assert query.unwanted.isEmpty();

        queryString = "test +query without -stopwords";
        query = Query.parse(queryString);
        assert "test".equals(query.all.get(0).get(0));
        assert "query".equals(query.all.get(0).get(1));
        assert "without".equals(query.all.get(1).get(0));
        assert "stopwords".equals(query.unwanted.toArray()[0]);
    }

    public void testParseAndSearch(Jedis conn) {
        System.out.println("\n----- testParseAndSearch -----");
        System.out.println("And now we are testing search...");
        Query.indexDocument(conn, "test", Query.CONTENT);

        Set<String> test = new HashSet<String>();
        test.add("test");

        String id = Query.parseAndSearch(conn, "content", 30);
        assert test.equals(conn.smembers("idx:" + id));

        id = Query.parseAndSearch(conn, "content indexed random", 30);
        assert test.equals(conn.smembers("idx:" + id));

        id = Query.parseAndSearch(conn, "content +indexed random", 30);
        assert test.equals(conn.smembers("idx:" + id));

        id = Query.parseAndSearch(conn, "content indexed +random", 30);
        assert test.equals(conn.smembers("idx:" + id));

        id = Query.parseAndSearch(conn, "content indexed -random", 30);
        assert conn.smembers("idx:" + id).isEmpty();

        id = Query.parseAndSearch(conn, "content indexed +random", 30);
        assert test.equals(conn.smembers("idx:" + id));

        System.out.println("Which passed!");
    }

    public void testSearchWithSort(Jedis conn) {
        System.out.println("\n----- testSearchWithSort -----");
        System.out.println("And now let's test searching with sorting...");

        Query. indexDocument(conn, "test", Query.CONTENT);
        Query.indexDocument(conn, "test2", Query.CONTENT);

        HashMap<String,String> values = new HashMap<String,String>();
        values.put("updated", "12345");
        values.put("id", "10");
        conn.hmset("kb:doc:test", values);

        values.put("updated", "54321");
        values.put("id", "1");
        conn.hmset("kb:doc:test2", values);

        SearchResult result = Query.searchAndSort(conn, "content", "-updated");
        assert "test2".equals(result.results.get(0));
        assert "test".equals(result.results.get(1));

        result = Query.searchAndSort(conn, "content", "-id");
        assert "test".equals(result.results.get(0));
        assert "test2".equals(result.results.get(1));

        System.out.println("Which passed!");
    }

    public void testSearchWithZsort(Jedis conn) {
        System.out.println("\n----- testSearchWithZsort -----");
        System.out.println("And now let's test searching with sorting via zset...");

        Query.indexDocument(conn, "test", Query.CONTENT);
        Query.indexDocument(conn, "test2", Query.CONTENT);

        conn.zadd("idx:sort:update", 12345, "test");
        conn.zadd("idx:sort:update", 54321, "test2");
        conn.zadd("idx:sort:votes", 10, "test");
        conn.zadd("idx:sort:votes", 1, "test2");

        Map<String,Integer> weights = new HashMap<String,Integer>();
        weights.put("update", 1);
        weights.put("vote", 0);
        SearchResult result = Query.searchAndZsort(conn, "content", false, weights);
        assert "test".equals(result.results.get(0));
        assert "test2".equals(result.results.get(1));

        weights.put("update", 0);
        weights.put("vote", 1);
        result = Query.searchAndZsort(conn, "content", false, weights);
        assert "test2".equals(result.results.get(0));
        assert "test".equals(result.results.get(1));
        System.out.println("Which passed!");
    }

    public void testStringToScore(Jedis conn) {
        System.out.println("\n----- testStringToScore -----");

        String[] words = "these are some words that will be sorted".split(" ");

        List<WordScore> pairs = new ArrayList<WordScore>();
        for (String word : words) {
            pairs.add(new WordScore(word, Query.stringToScore(word)));
        }
        List<WordScore> pairs2 = new ArrayList<WordScore>(pairs);
        Collections.sort(pairs);
        Collections.sort(pairs2, new Comparator<WordScore>(){
            public int compare(WordScore o1, WordScore o2){
                long diff = o1.score - o2.score;
                return diff < 0 ? -1 : diff > 0 ? 1 : 0;
            }
        });
        assert pairs.equals(pairs2);

        Map<Integer,Integer> lower = new HashMap<Integer,Integer>();
        lower.put(-1, -1);
        int start = (int)'a';
        int end = (int)'z';
        for (int i = start ; i <= end; i++){
            lower.put(i, i - start);
        }

        words = "these are some words that will be sorted".split(" ");
        pairs = new ArrayList<WordScore>();
        for (String word : words) {
            pairs.add(new WordScore(word, Query.stringToScoreGeneric(word, lower)));
        }
        pairs2 = new ArrayList<WordScore>(pairs);
        Collections.sort(pairs);
        Collections.sort(pairs2, new Comparator<WordScore>(){
            public int compare(WordScore o1, WordScore o2){
                long diff = o1.score - o2.score;
                return diff < 0 ? -1 : diff > 0 ? 1 : 0;
            }
        });
        assert pairs.equals(pairs2);

        Map<String,String> values = new HashMap<String,String>();
        values.put("test", "value");
        values.put("test2", "other");
        Query.zaddString(conn, "key", values);
        assert conn.zscore("key", "test") == Query.stringToScore("value");
        assert conn.zscore("key", "test2") == Query.stringToScore("other");
    }

    public void testIndexAndTargetAds(Jedis conn) {
        System.out.println("\n----- testIndexAndTargetAds -----");
        Query.indexAd(conn, "1", new String[]{"USA", "CA"}, Query.CONTENT, Ecpm.CPC, .25);
        Query.indexAd(conn, "2", new String[]{"USA", "VA"}, Query.CONTENT + " wooooo", Ecpm.CPC, .125);

        String[] usa = new String[]{"USA"};
        for (int i = 0; i < 100; i++) {
        	Query.targetAds(conn, usa, Query.CONTENT);
        }
        Pair<Long,String> result = Query.targetAds(conn, usa, Query.CONTENT);
        long targetId = result.getKey();
        String adId = result.getValue();
        assert "1".equals(result.getValue());

        result = Query.targetAds(conn, new String[]{"VA"}, "wooooo");
        assert "2".equals(result.getValue());

        Iterator<Tuple> range = conn.zrangeWithScores("idx:ad:value:", 0, -1).iterator();
        assert new Tuple("2", 0.125).equals(range.next());
        assert new Tuple("1", 0.25).equals(range.next());

        range = conn.zrangeWithScores("ad:base_value:", 0, -1).iterator();
        assert new Tuple("2", 0.125).equals(range.next());
        assert new Tuple("1", 0.25).equals(range.next());

        Query.recordClick(conn, targetId, adId, false);

        range = conn.zrangeWithScores("idx:ad:value:", 0, -1).iterator();
        assert new Tuple("2", 0.125).equals(range.next());
        assert new Tuple("1", 2.5).equals(range.next());

        range = conn.zrangeWithScores("ad:base_value:", 0, -1).iterator();
        assert new Tuple("2", 0.125).equals(range.next());
        assert new Tuple("1", 0.25).equals(range.next());
    }

    public void testIsQualifiedForJob(Jedis conn) {
        System.out.println("\n----- testIsQualifiedForJob -----");
        Query.addJob(conn, "test", "q1", "q2", "q3");
        assert Query.isQualified(conn, "test", "q1", "q3", "q2");
        assert !Query.isQualified(conn, "test", "q1", "q2");
    }

    public void testIndexAndFindJobs(Jedis conn) {
        System.out.println("\n----- testIndexAndFindJobs -----");
        Query.indexJob(conn, "test1", "q1", "q2", "q3");
        Query. indexJob(conn, "test2", "q1", "q3", "q4");
        Query.indexJob(conn, "test3", "q1", "q3", "q5");

        assert Query.findJobs(conn, "q1").size() == 0;

        Iterator<String> result = Query.findJobs(conn, "q1", "q3", "q4").iterator();
        assert "test2".equals(result.next());

        result = Query.findJobs(conn, "q1", "q3", "q5").iterator();
        assert "test3".equals(result.next());

        result = Query.findJobs(conn, "q1", "q2", "q3", "q4", "q5").iterator();
        assert "test1".equals(result.next());
        assert "test2".equals(result.next());
        assert "test3".equals(result.next());
    }
}
